/**
 * Copyright(c) Live2D Inc. All rights reserved.
 *
 * Use of this source code is governed by the Live2D Open Software license
 * that can be found at https://www.live2d.com/eula/live2d-open-software-license-agreement_en.html.
 */

#include "CubismEyeBlink.hpp"

#include "Id/CubismId.hpp"

#include <stdlib.h>

namespace Live2D
{
    namespace Cubism
    {
        namespace Framework
        {

            /**
             * IDで指定された目のパラメータが、0のときに閉じるなら true 、1の時に閉じるなら false 。
             */
            const bool CloseIfZero = true;

            CubismEyeBlink *CubismEyeBlink::Create(ICubismModelSetting *modelSetting)
            {
                return CSM_NEW CubismEyeBlink(modelSetting);
            }

            void CubismEyeBlink::Delete(CubismEyeBlink *eyeBlink)
            {
                CSM_DELETE_SELF(CubismEyeBlink, eyeBlink);
            }

            CubismEyeBlink::CubismEyeBlink(ICubismModelSetting *modelSetting)
                : _blinkingState(EyeState_First), _nextBlinkingTime(0.0f), _stateStartTimeSeconds(0.0f), _blinkingIntervalSeconds(4.0f),
                  _closingSeconds(0.1f), _closedSeconds(0.05f), _openingSeconds(0.15f), _userTimeSeconds(0.0f)
            {
                if (modelSetting == NULL)
                {
                    return;
                }

                for (int i = 0; i < modelSetting->GetEyeBlinkParameterCount(); ++i)
                {
                    _parameterIds.push_back(modelSetting->GetEyeBlinkParameterId(i));
                }
            }

            CubismEyeBlink::~CubismEyeBlink()
            {
            }

            csmFloat32 CubismEyeBlink::DeterminNextBlinkingTiming() const
            {
                const csmFloat32 r = static_cast<csmFloat32>(rand()) / (float) RAND_MAX;

                return _userTimeSeconds + (r * (2.0f * _blinkingIntervalSeconds - 1.0f));
            }

            void CubismEyeBlink::SetBlinkingInterval(csmFloat32 blinkigInterval)
            {
                _blinkingIntervalSeconds = blinkigInterval;
            }

            void CubismEyeBlink::SetBlinkingSettings(csmFloat32 closing, csmFloat32 closed, csmFloat32 opening)
            {
                _closingSeconds = closing;
                _closedSeconds = closed;
                _openingSeconds = opening;
            }

            void CubismEyeBlink::SetParameterIds(const QVector<CubismIdHandle> &parameterIds)
            {
                _parameterIds = parameterIds;
            }

            const QVector<CubismIdHandle> &CubismEyeBlink::GetParameterIds() const
            {
                return _parameterIds;
            }

            void CubismEyeBlink::UpdateParameters(CubismModel *model, csmFloat32 deltaTimeSeconds)
            {
                _userTimeSeconds += deltaTimeSeconds;
                csmFloat32 parameterValue;
                csmFloat32 t = 0.0f;

                switch (_blinkingState)
                {
                    case EyeState_Closing:
                        t = ((_userTimeSeconds - _stateStartTimeSeconds) / _closingSeconds);

                        if (t >= 1.0f)
                        {
                            t = 1.0f;
                            _blinkingState = EyeState_Closed;
                            _stateStartTimeSeconds = _userTimeSeconds;
                        }

                        parameterValue = 1.0f - t;

                        break;
                    case EyeState_Closed:
                        t = ((_userTimeSeconds - _stateStartTimeSeconds) / _closedSeconds);

                        if (t >= 1.0f)
                        {
                            _blinkingState = EyeState_Opening;
                            _stateStartTimeSeconds = _userTimeSeconds;
                        }

                        parameterValue = 0.0f;

                        break;
                    case EyeState_Opening:
                        t = ((_userTimeSeconds - _stateStartTimeSeconds) / _openingSeconds);

                        if (t >= 1.0f)
                        {
                            t = 1.0f;
                            _blinkingState = EyeState_Interval;
                            _nextBlinkingTime = DeterminNextBlinkingTiming();
                        }

                        parameterValue = t;

                        break;
                    case EyeState_Interval:
                        if (_nextBlinkingTime < _userTimeSeconds)
                        {
                            _blinkingState = EyeState_Closing;
                            _stateStartTimeSeconds = _userTimeSeconds;
                        }

                        parameterValue = 1.0f;

                        break;
                    case EyeState_First:
                    default:
                        _blinkingState = EyeState_Interval;
                        _nextBlinkingTime = DeterminNextBlinkingTiming();

                        parameterValue = 1.0f;

                        break;
                }

                if (!CloseIfZero)
                {
                    parameterValue = -parameterValue;
                }

                for (auto i = 0; i < _parameterIds.size(); ++i)
                {
                    model->SetParameterValue(_parameterIds[i], parameterValue);
                }
            }

        } // namespace Framework
    }     // namespace Cubism
} // namespace Live2D
